﻿using UnityEngine;
using System.Collections;
using LitJson;
using System;
using System.Collections.Generic;
using SongeNS;
using NewEditorNS;
using SimpleJSON;
using Jhqc.EditorCommon;

public enum EnumCompType { normal, wall, greenMesh }

public enum EnumModelState { empty, creating, created }

/// <summary>实现组件 (墙体+普通)(原组合组件归入普通组件一类 normal)</summary>
public class CompVO : MonoBehaviour, IJsonData, IMouseCtrl, ISelectable
{
    public EnumCompType CompType;
    public string CompCode;

    public WallVO theWallVO;

    #region 模型与显示
    /// <summary>模型容器</summary>
    GameObject modelContent;
    GameObject modelPrefab;


    EnumModelState theModelState = EnumModelState.empty;

    /// <summary>
    /// 模型是否加载完成
    /// </summary>
    public bool IsLoaded
    {
        get
        {
            return theModelState == EnumModelState.created;
        }
    }

    bool showModel = false;
    public bool ShowModel
    {
        get
        {
            return showModel;
        }

        set
        {
            bool oldValue = showModel;
            showModel = value;

            if (!showModel)
                disposeModel();
            else if (theModelState == EnumModelState.empty)
                loadModel(createModel, createBrokenModel);

            //TEST
            //if (modelContent == null)
            //{
            //    modelContent = GameObject.CreatePrimitive(PrimitiveType.Cube);
            //    modelContent.transform.parent = transform;
            //    modelContent.transform.localPosition = Vector3.zero;
            //    modelContent.transform.localScale = new Vector3(10, 10, 10);
            //}
            //if (showModel && !oldValue)
            //    ColorManager.GetIns().ChangeColor(modelContent, Color.red);
            //else if (!showModel && oldValue)
            //    ColorManager.GetIns().RevertColor(modelContent);

        }
    }

    public void ResetPrefab(string compCode, float wallLength)
    {
        disposeModel();
        theWallVO.WallLength = wallLength;
        CompCode = compCode;
        theWallVO.WallType = CompCode;
        modelPrefab = null;
        loadModel(createModel, createBrokenModel);
    }

    bool showSelect;
    public bool ShowSelect
    {
        get
        {
            return showSelect;
        }

        set
        {
            if (this == null)
                return;
            if (gameObject == null)
                return;
            showSelect = value;
            if (showSelect)
                ColorManager.GetIns().ChangeColor(gameObject, ColorManager.Orange);
            else
                ColorManager.GetIns().RevertColor(gameObject);
        }
    }

    void disposeModel()
    {
        if (theModelState == EnumModelState.created)
        {
            if (modelContent != null)
                Destroy(modelContent);
        }
        theModelState = EnumModelState.empty;
    }

    public void RefreshModel()
    {
        createModel();
    }

    void loadModel(Action onLoaded, Action<HttpResp.ErrorType, string> onLoadFailed)
    {
        theModelState = EnumModelState.creating;


        if (CompType == EnumCompType.greenMesh)
            onLoaded();
        else if (modelPrefab != null)
            onLoaded();
        else
        {

            //TEST 
            //modelPrefab = CompLib.BrokenPrefab;
            //onLoaded();
            //TEST 
            CompLib.Instance.GetPrefab(CompCode, go =>
            {
                string cc = CompCode;
                modelPrefab = go;
                onLoaded();


            }, createBrokenModel);
        }
    }

    private void AdjustDefMat(Transform _tf)
    {
        if (_tf == null)
        {
            return;
        }

        MeshRenderer mr = _tf.GetComponent<MeshRenderer>();
        if (mr != null)
        {
            Material[] ms = mr.materials;
            for (int i = 0; i < ms.Length;++i )
            {
               Material curM = ms[i];
               ProceduralMaterial pm = curM as ProceduralMaterial;
               if (pm == null)
               {
                   //Debug.Log("对象："+_tf.gameObject.name+"----当前材质shader名称："+ curM.shader.name);
                   curM.SetFloat("_Glossiness", 0);
               }
            }
        }

        for (int i = 0; i < _tf.childCount;++i )
        {
            AdjustDefMat(_tf.GetChild(i));
        }
    }

    /// <summary>创建加载失败的模型</summary>
    void createBrokenModel(HttpResp.ErrorType errorType, string errorInfo)
    {
        InfoTips.LogInfo("加载组件模型出错! CompCode: " + CompCode + ", 错误类型: " + errorType.ToString() + ", 错误信息: " + errorInfo);
        modelPrefab = CompLib.BrokenPrefab;
        modelContent = Instantiate(modelPrefab);
        modelContent.transform.parent = transform;
        modelContent.transform.localPosition = Vector3.zero;
        modelContent.name = "模型损坏";
    }

    void createModel()
    {
        if (!ShowModel)
        {
            theModelState = EnumModelState.empty;
            return;
        }

        //#region TEST 160916 测试按距离显示模型

        //if (modelContent != null)
        //    Destroy(modelContent);


        //modelContent = GameObject.CreatePrimitive(PrimitiveType.Cube);

        //modelContent.SetActive(true);
        //modelContent.transform.parent = transform;
        //modelContent.transform.localPosition = Vector3.zero;
        //modelContent.transform.localEulerAngles = new Vector3(-90f, 0f, 0f);
        //modelContent.transform.localScale = Vector3.one;
        //modelContent.name = "content";

        //theModelState = EnumModelState.created;
        //ColorManager.GetIns().ChangeColor(gameObject, Color.white);
        //return;


        //#endregion

        if (modelContent != null)
            Destroy(modelContent);

        if (CompType == EnumCompType.normal)
            createNormalContent();
        else if (CompType == EnumCompType.wall)
            createWallContent();
        else if (CompType == EnumCompType.greenMesh)
            createWallContent();

        //CHECK songyi 160921 对每一个Substence类型的子模型 进行reBuildMat
        //NOTE 不用rebuild, 但需要CompLib在原件完成rebuild(endOfFrame)之后才获取组件原件
        

        //CHECK 调整默认材质参数
        AdjustDefMat(modelContent.transform);


        theModelState = EnumModelState.created;
    }

    void createNormalContent()
    {
        if (modelContent != null)
            Destroy(modelContent);
        if (modelPrefab != null)
            modelContent = Instantiate(modelPrefab);
        else
        {
            modelContent = GameObject.CreatePrimitive(PrimitiveType.Cube);
            modelContent.name = "模型损坏";
        }

        modelContent.SetActive(true);
        modelContent.transform.parent = transform;
        modelContent.transform.localPosition = Vector3.zero;
        modelContent.transform.localEulerAngles = new Vector3(-90f, 0f, 0f);
        modelContent.transform.localScale = Vector3.one;
        modelContent.name = "content";
    }

    void createWallContent()
    {
        if (modelContent != null)
            Destroy(modelContent);
        if (CompType == EnumCompType.wall)
        {
            modelContent = new GameObject();
            modelContent.transform.parent = gameObject.transform;
            modelContent.name = "wallModelContent";
            for (int c = 0, len = theWallVO.PointList.Count; c < len - 1; c++)
            {
                Vector3 fromPos = theWallVO.PointList[c];
                Vector3 toPos = theWallVO.PointList[c + 1];
                Vector3 dir = toPos - fromPos;
                float rotY = SongeUtil.GetRotateY(dir);
                float rotV = SongeUtil.GetRotatV(dir);
                for (float curLength = 0; curLength < dir.magnitude; curLength += theWallVO.WallLength)
                {
                    float lastLengthRatio = (dir.magnitude - curLength) / theWallVO.WallLength;
                    float wallScale;
                    if (theWallVO.needScaleTail)
                        wallScale = lastLengthRatio > 1.5f ? 1f : lastLengthRatio;
                    else
                        wallScale = 1f;


                    Vector3 wallPos = Vector3.Lerp(fromPos, toPos, (curLength + theWallVO.WallLength * wallScale / 2) / dir.magnitude);
                    addWallModel(modelPrefab, wallPos, rotY, rotV, wallScale, modelContent, theWallVO.WallType);
                }
                //modelPrefab.SetActive(false);
                //Destroy(modelPrefab);
            }
        }
        else if (CompType == EnumCompType.greenMesh)
        {
            modelContent = new GameObject();
            modelContent.transform.parent = gameObject.transform;

            Texture tex = Texture2D.whiteTexture;
            if (theWallVO.PointList.Count < 3)
                return;

            List<Vector3> pointList = new List<Vector3>();
            for (int c = 0, len = theWallVO.PointList.Count; c < len; c++)
            {
                pointList.Add(theWallVO.PointList[c]);
            }
            float tile = 5.0f;
            GameObject greenUp = BuildMesh.Create(pointList, tex, tile);
            greenUp.name = "greenUp";
            greenUp.transform.parent = modelContent.transform;
            greenUp.AddComponent<MeshCollider>();

            pointList.Reverse();
            GameObject greenDown = BuildMesh.Create(pointList, tex, tile);
            greenDown.name = "greenDown";
            greenDown.transform.parent = modelContent.transform;
            greenDown.AddComponent<MeshCollider>();

            RefreshGreenMeshMat();
        }
    }

    /// <summary>添加单个围墙片段</summary>
    void addWallModel(GameObject prefrab, Vector3 pos, float rotY, float rotV, float scaleY, GameObject parent, string wallType/*, bool isAutoSetY*/)
    {
        //GameObject wallContent = new GameObject();
        //wallContent.transform.parent = parent.transform;
        //wallContent.transform.localPosition = Vector3.zero;
        //wallContent.transform.localEulerAngles = new Vector3(-90f, 0f, 0f);
        //wallContent.name = "wallContent";

        GameObject newWall;
        if (prefrab == null)
        {
            newWall = GameObject.CreatePrimitive(PrimitiveType.Cube);
            newWall.name = "模型损坏";
        }
        else
            newWall = Instantiate(prefrab);
        newWall.SetActive(true);
        float rotX = 0f;
        newWall.transform.rotation = Quaternion.Euler(rotX - 90f, rotY + 90f, 0);
        newWall.transform.Rotate(Vector3.up, rotV, Space.Self);
        newWall.transform.position = pos;
        newWall.transform.localScale = new Vector3(scaleY, 1, 1);
        newWall.transform.parent = parent.transform;
        Collider col = newWall.GetComponent<Collider>();
        if (col != null)
            col.enabled = false;
    }

    public void CreatePointList()
    {
        //TODO 160916 检查是否原来就有CtrlPoint 并删除
        if (CompType == EnumCompType.wall || CompType == EnumCompType.greenMesh)
        {
            for (int i = 0, length = theWallVO.PointList.Count; i < length; i++)
            {
                Vector3 pos = theWallVO.PointList[i];

                CtrlPoint newPoint = CtrlPointManager.Instance.AddCtrlPoint(this, pos, null, false, false);
            }
        }
    }

    public void SetCompVisibleByCameraPos(Vector3 camPos)
    {
        float loadLength = SceneManager.Instance.ConfigJD.ReadFloat("loadLength", 300f);
        float unloadLength = SceneManager.Instance.ConfigJD.ReadFloat("unloadLength", 500f);

        Bounds loadBound = new Bounds(camPos, new Vector3(loadLength, 9999f, loadLength));
        Bounds unloadBound = new Bounds(camPos, new Vector3(unloadLength, 9999f, unloadLength));

        if (CompType == EnumCompType.normal)
        {
            bool loadFlag = loadBound.Contains(transform.position);
            bool unloadFlag = !unloadBound.Contains(transform.position);

            if (loadFlag && theModelState == EnumModelState.empty)
            {
                //CreateModel();
                ShowModel = true;
            }
            else if (unloadFlag)
            {
                //DisposeModel();
                ShowModel = false;
            }
        }
        else if (CompType == EnumCompType.greenMesh || CompType == EnumCompType.wall)
        {
            //bool totalLoadFlag = loadBound.Contains(theWallVO.FirstPoint);//原版只判断第一个点
            //CHECK 160712 修复对墙体的加载判断, 当墙体的任一控制点出现在加载范围内时, 进行加载
            bool totalLoadFlag = false;
            for (int iPoint = 0, nPoint = theWallVO.PointList.Count; iPoint < nPoint; iPoint++)
            {
                Vector3 tarPoint = theWallVO.PointList[iPoint];
                if (loadBound.Contains(theWallVO.FirstPoint))
                {
                    totalLoadFlag = true;
                    break;
                }
            }

            if (totalLoadFlag)
            {
                ShowModel = true;
            }

            bool isAllOut = true;

            List<CtrlPoint> pointList = CtrlPointManager.Instance.PointDic[this];
            for (int i = 0; i < pointList.Count; i++)
            {
                bool loadFlag = loadBound.Contains(pointList[i].transform.position);
                bool unloadFlag = !unloadBound.Contains(pointList[i].transform.position);
                if (!unloadFlag)
                    isAllOut = false;

                //setModelRendererVisible(child, loadFlag);
            }
            if (isAllOut)
                ShowModel = false;
        }
    }

    void setModelRendererVisible(GameObject go, bool flag)
    {
        if (go != null)
        {
            SongeUtil.forAllChildren(go, tar =>
            {
                Renderer r = tar.GetComponent<Renderer>();
                if (r != null)
                {
                    r.enabled = flag;
                }
            });
        }
    }

    #endregion

    #region data

    public IJsonData ReadJsonData(JsonData jd)
    {
        CompType = jd.ReadEnum<EnumCompType>("CompType");
        if (CompType == EnumCompType.normal)
        {
            CompCode = jd.ReadString("CompCode");
            transform.position = jd.ReadVec3("p");
            transform.eulerAngles = jd.ReadVec3("e");
            transform.localScale = jd.ReadVec3("s");

        }
        else if (CompType == EnumCompType.wall)
        {
            CompCode = jd.ReadString("wallType");
            theWallVO = new WallVO();
            theWallVO.WallType = CompCode;
            theWallVO.WallLength = jd.ReadFloat("wallLength");//float.Parse(jd["wallLength"].ToString());
            theWallVO.FirstPoint = jd.ReadVec3("firstPoint");

            theWallVO.PointList = new List<Vector3>();
            JsonData pointListJD = jd["pointList"];
            for (int j = 0; j < pointListJD.Count; j++)
            {
                Vector3 pointPos = JsonUtils.StringToVector3(jd["pointList"][j].ToString());
                theWallVO.PointList.Add(pointPos);
            }
            theWallVO.needScaleTail = jd.ReadBool("needScaleTail", true);

            transform.position = JsonUtils.StringToVector3(jd["firstPoint"].ToString());
        }
        else if (CompType == EnumCompType.greenMesh)
        {
            theWallVO = new WallVO();
            theWallVO.WallType = CompCode;
            theWallVO.WallLength = jd.ReadFloat("wallLength");//float.Parse(jd["wallLength"].ToString());
            theWallVO.FirstPoint = jd.ReadVec3("firstPoint");

            theWallVO.PointList = new List<Vector3>();
            JsonData pointListJD = jd["pointList"];
            for (int j = 0; j < pointListJD.Count; j++)
            {
                Vector3 pointPos = JsonUtils.StringToVector3(jd["pointList"][j].ToString());
                theWallVO.PointList.Add(pointPos);
            }

            transform.position = JsonUtils.StringToVector3(jd["firstPoint"].ToString());

            //gMat = JSONClass.Parse(jd.ReadJsonData("greenMeshMat").ToString());
            greenMeshMat = jd.ReadJsonData("greenMeshMat");

        }

        return this;
    }

    public JsonData ToJsonData()
    {
        JsonData jd = new JsonData();
        jd["CompType"] = CompType.ToString();
        if (CompType == EnumCompType.normal)
        {
            jd["CompCode"] = CompCode;
            jd["p"] = JsonUtils.vecToStr(transform.position);
            jd["e"] = JsonUtils.vecToStr(transform.eulerAngles);
            jd["s"] = JsonUtils.vecToStr(transform.localScale);
        }
        else if (CompType == EnumCompType.wall)
        {
            jd["wallType"] = CompCode;
            jd["wallLength"] = theWallVO.WallLength;

            List<string> listRes = new List<string>();
            for (int j = 0, l = theWallVO.PointList.Count; j < l; j++)
            {
                Vector3 point = theWallVO.PointList[j];
                if (j == 0)
                    jd["firstPoint"] = JsonUtils.vecToStr(point);
                listRes.Add(JsonUtils.vecToStr(point));
            }
            jd["pointList"] = JsonMapper.ToObject(JsonMapper.ToJson(listRes));
            jd["needScaleTail"] = theWallVO.needScaleTail.ToString();
        }
        else if (CompType == EnumCompType.greenMesh)
        {
            jd["wallType"] = CompCode;

            //解析材质
            jd["greenMeshMat"] = greenMeshMat;

            List<string> listRes = new List<string>();
            for (int j = 0, l = theWallVO.PointList.Count; j < l; j++)
            {
                Vector3 point = theWallVO.PointList[j];
                if (j == 0)
                    jd["firstPoint"] = JsonUtils.vecToStr(point);
                listRes.Add(JsonUtils.vecToStr(point));
            }
            jd["pointList"] = JsonMapper.ToObject(JsonMapper.ToJson(listRes));
        }

        return jd;
    }
    #endregion

    public JsonData greenMeshMat = new JsonData();
    //JSONClass gMat;
    /// <summary>greenMesh组件记录当前材质</summary>
    public void RecordGreenMeshMat(GameObject tar)
    {
        MeshRenderer render = tar.GetComponent<MeshRenderer>();
        if (render != null)
        {
            CityMaterial cityMat = new CityMaterial(render.material
                as ProceduralMaterial);
            JSONClass gMat;
            gMat = cityMat.ToJsonNode() as JSONClass;
            string test = gMat.ToString();
            greenMeshMat = JsonMapper.ToObject(gMat.ToString());
        }
        else
        {
            //gMat = new JSONClass();
            greenMeshMat = new JsonData();
        }
        RefreshGreenMeshMat();//当为一面设置时, 为另一面也设置上材质
    }

    public void RefreshGreenMeshMat()
    {
        if (greenMeshMat != null)
            SongeUtil.forAllChildren(gameObject, tar =>
            {
                JSONClass jc = greenMeshMat.ToSimpleJsonNode();
                if (tar.GetComponent<MeshRenderer>() != null)
                    CityMaterial.SetMaterial(jc, tar);
            });
    }

    #region MouseCtrl
    public void MouseDown(Vector2 screenPoint, Vector3 worldPoint, int mouse)
    {
    }

    public void MouseUp(Vector2 screenPoint, Vector3 worldPoint, int mouse)
    {
    }

    public void MouseClick(Vector2 screenPoint, Vector3 worldPoint, int mouse)
    {
    }

    public void MouseDoubleClick(Vector2 screenPoint, Vector3 worldPoint, int mouse)
    {
    }

    public void OnDragStart(Vector2 screenPoint, Vector3 worldPoint, int mouse)
    {
    }

    public void OnDraging(Vector2 screenPoint, Vector3 worldPoint, int mouse)
    {
    }

    public void OnDragEnd(Vector2 screenPoint, Vector3 worldPoint, int mouse)
    {
    }

    #endregion
}

public class WallVO
{
    static float MinWallLength = 0.2f;
    static WallVO CheckWallLength(WallVO wall)
    {
        wall.wallLength = Mathf.Max(MinWallLength, wall.wallLength);
        return wall;
    }

    public string WallType = "default";
    float wallLength = MinWallLength;
    public float WallLength
    {
        get { return wallLength; }
        set
        {
            wallLength = value;
            wallLength = Mathf.Max(MinWallLength, wallLength);
        }
    }
    public Vector3 FirstPoint;
    public List<Vector3> PointList;
    public bool needScaleTail = true;
}